package com.ruili.framework.okhttp;

import com.alibaba.fastjson2.JSONObject;
import com.ruili.framework.exception.HttpStatusException;
import okhttp3.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;

import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class HttpUtils {

    private static final Logger logger = LoggerFactory.getLogger(HttpUtils.class);

    private static OkHttpClient client;

    private static final String MEDIA_TYPE_JSON_UTF8 = "application/json; charset=utf-8";

    private static final int CONNECT_TIMEOUT = 5;

    private static final int READ_TIMEOUT = 7;

    /**
     * 单例模式  获取类实例
     *
     * @return client
     */
    private static OkHttpClient getInstance() {
        if (client == null) {
            synchronized (OkHttpClient.class) {
                if (client == null) {
                    client = new OkHttpClient.Builder()
                            .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
                            .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
                            .build();
                }
            }
        }
        return client;
    }

    /**
     * 这个方法暂时不做字符集处理，因为如果响应头中有设置字符集就用响应头中的，如果没有默认的是用UTF-8
     * @param url 请求的完整URL
     * @return 响应的内容
     * @throws HttpStatusException 非200会抛异常出来
     */
    public static String doGet(String url) throws HttpStatusException {

        try {
            StopWatch stopWatch =StopWatch.createStarted();
            logger.info("request url:{}" , url);
            Request request = new Request.Builder().url(url).build();
            // 创建一个请求
            Response response = getInstance().newCall(request).execute();
            int httpCode = response.code();
            String result;
            ResponseBody body = response.body();
            if (body != null) {
                result = body.string();
                stopWatch.stop();
                logger.info("response body:{} , cost time:{}" , result , stopWatch.getTime());
            } else {
                response.close();
                result =StringUtils.EMPTY;
            }
            return handleHttpResponse(httpCode, result);
        } catch (Exception ex) {
            handleHttpThrowable(ex, url);
            return StringUtils.EMPTY;
        }
    }

    /**
     * 请求体中放的是JSON
     * @param url 请求的RUL
     * @param postBody 请求的JSON串
     * @return 返回的JSONObject对象
     * @throws HttpStatusException
     */
    public static JSONObject doPostJsonString(String url, String postBody) throws HttpStatusException {
        String result = doPostString(url, postBody, MEDIA_TYPE_JSON_UTF8);
        return JSONObject.parseObject(result);
    }

    /**
     * 参数放在请求体中传输
     * @param url 请求的URL
     * @param postBody 请求体中内容
     * @param mediaType 请求头中的MediaType 类型
     * @return 响应内容
     * @throws HttpStatusException 响应不为200时抛出异常
     */
    public static String doPostString(String url, String postBody, String mediaType) throws HttpStatusException {
        try {
            MediaType createMediaType = MediaType.parse(mediaType == null ? MEDIA_TYPE_JSON_UTF8 : mediaType);
            Request request = new Request.Builder()
                    .url(url)
                    .post(RequestBody.create(postBody ,createMediaType))
                    .build();

            Response response = getInstance().newCall(request).execute();
            int httpCode = response.code();
            String result;
            ResponseBody body = response.body();
            if (body != null) {
                result = body.string();
            } else {
                response.close();
                result = StringUtils.EMPTY;
            }
            return handleHttpResponse(httpCode, result);
        } catch (Exception ex) {
            handleHttpThrowable(ex, url);
            return StringUtils.EMPTY;
        }
    }

    /**
     * form 表单形式提交
     * @param url
     * @param parameterMap
     * @return
     * @throws HttpStatusException
     */
    public static String doPostForm(String url, Map<String, String> parameterMap) throws HttpStatusException {
        try {
            FormBody.Builder builder = new FormBody.Builder();
            if (!parameterMap.isEmpty()) {
                for (Map.Entry<String, String> entry : parameterMap.entrySet()) {
                    String parameterName = entry.getKey();
                    String value = entry.getValue();
                    builder.add(parameterName, value);
                }
            }
            FormBody formBody = builder.build();;

            Request request = new Request.Builder()
                    .url(url)
                    .post(formBody)
                    .build();

            Response response = getInstance().newCall(request).execute();
            String result;
            int httpCode = response.code();
            ResponseBody body = response.body();
            if (body != null) {
                result = body.string();
            } else {
                response.close();
                throw new IOException("exception in OkHttpUtil,response body is null");
            }
            return handleHttpResponse(httpCode, result);

        } catch (Exception ex) {
            handleHttpThrowable(ex, url);
            return StringUtils.EMPTY;
        }
    }

    private static String handleHttpResponse(int httpCode, String result) throws HttpStatusException {
        if (httpCode == HttpStatus.OK.value()) {
            return result;
        }
        HttpStatus httpStatus = HttpStatus.valueOf(httpCode);
        throw new HttpStatusException(httpStatus);
    }

    private static void handleHttpThrowable(Exception ex, String url) throws HttpStatusException {
        if (ex instanceof HttpStatusException) {
            throw (HttpStatusException) ex;
        }
        logger.error("OkHttp error url: " + url, ex);
        if (ex instanceof SocketTimeoutException) {
            throw new RuntimeException("request time out of OkHttp when do url:" + url);
        }
        throw new RuntimeException(ex);
    }
}
